# 테스트 기본 이해하기

# 개념

테스트는 이름 그대로, 소프트웨어를 테스트하는 작업입니다. 소프트웨어에 숨겨진 버그는 없는지, 여러 명이 동시에 이용할 때에도 잘 견디는지 등의 문제들을 꼼꼼히 점검하는 일입니다. 테스트를 거치지 않은 소프트웨어는 일반적으로 신뢰하기 어렵습니다. 실시간으로 언제 어디서 버그가 나올지 모릅니다.

이때 테스트 코드를 잘 작성해 둔다면, 버그에 견고한 코드를 작성할 수 있습니다. 같은 팀 개발자는 테스트를 보는 것만으로도 프로젝트의 전체적인 기능과 구조를 파악할 수 있게 됩니다.

# 테스트의 종류

개발자가 가장 많이 마주하게 되는 테스트는 크게 세 가지로 아래와 같습니다.

image-20210915211344836

  • 유닛 테스트
    • 유닛(Unit)이라는 말 그대로, 가장 작은 단위의 테스트입니다.
    • 단일 기능을 가지는 함수, 클래스의 메서드가 잘 작동하는지 확인합니다.
    • 테스트하고자 하는 코드는 다른 외부 컴포넌트(웹 서버, DB 등)에 의존성이 없어야 합니다.
    • 가장 간단하고, 직관적이며, 빠르게 실행과 결과를 볼 수 있는 테스트입니다.
  • 통합 테스트
    • 통합(Integration)이라는 말 그대로, 여러 요소를 통합한 테스트를 말합니다.
    • 데이터베이스와 연동한 코드가 잘 작동하는지, 여러 함수와 클래스가 엮인 로직이 잘 작동하는지 등을 확인합니다.
    • 유닛 테스트보다는 복잡하고 느리지만, 소프트웨어는 결국 여러 코드 로직의 통합이라는 점에서 통합 테스트 역시 중요합니다.
  • E2E 테스트
    • E2E는 End To End의 약자로, 끝에서 끝, 즉 클라이언트 입장에서 테스트해보는 것입니다.
    • 예를 들어 쇼핑몰 웹사이트의 경우, /login 으로 POST 요청 시 로그인은 잘 되는지, /order 로 POST 요청 시 주문 결과는 잘 나오는지 등을 확인합니다.
    • 보통 유저 시나리오에 따라 테스트합니다.
    • 테스트 중 가장 느리지만, 결국 소프트웨어를 사용하는건 유저이고, 유저 입장에서 해보는 테스트이므로, 역시 중요하다고 할 수 있습니다.

보통 테스트는 유닛 -> 통합 -> E2E 순으로 작성하게 됩니다(꼭 정답이 있는 건 아닙니다) 작은 단위부터 테스트를 작성하면서 점점 통합적인 테스트를 진행하게 됩니다.

테스트 개수는 가장 작은 단위 테스트인 유닛 테스트가 가장 많고, E2E 테스트가 가장 적게 됩니다.

유닛 테스트로 추후에 어떠한 컴포넌트의 기능이 문제가 있는지 빠르게 찾아낼 수 있습니다. 한편 통합테스트로 프로그램의 로직 흐름에 이상이 없는지를 파악할 수 있습니다. E2E 테스트는 최종적으로 사용자 관점에서 사용하기에 기능적인 문제가 없는지 진행하는 테스트로 정리할 수 있습니다.

# 테스트 코드

테스트의 종류에 대해서 얼핏 알았습니다. 이제 직접 위에서 배운 테스트를 실습해봅시다!
그런데 어떻게 테스트를 해볼 수 있을까요?

테스트 역시 코드로 구현할 수 있습니다. 이 강의에서는 파이썬의 대표적인 테스트 프레임워크인 pytest를 사용하겠습니다.

우선 pytest를 설치합니다.

$ pip install pytest

# test_example.py

# 테스트 대상이 되는 함수입니다.
def add(a: int, b: int) -> int:
    return a + b

# 테스트를 시행할 코드입니다.
def test_add():
    assert add(1, 1) == 2  # add(1, 1)의 출력이 2면 테스트를 통과합니다.

이제 셸에서 pytest {파일 이름} 명령어로 위에서 작성한 테스트 코드를 실행시킬 수 있습니다.

$ python -m pytest test_example.py

========================== test session starts ==========================
platform darwin -- Python 3.8.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- 
cachedir: .pytest_cache
collected 1 item                                                                                                                                                                               

test_example.py::test_add PASSED  # 테스트 통과에 성공했습니다.

add(1, 1) 의 출력이 2 였기 때문에 테스트가 통과했습니다.
만약 출력값을 2 가 아니라 3 이랑 같은지 비교하면 어떻게 될까요? 코드를 수정해봅시다.

# test_example.py

def add(a: int, b: int) -> int:
    return a + b

def test_add():
    assert add(1, 1) == 3  # 2 -> 3으로 수정

그리고 다시 다음처럼 pytest 명령어를 실행합니다.

$ pythom -m pytest test_example.py      

========================== test session starts ==========================
platform darwin -- Python 3.8.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
collected 1 item                                                                                                                                                                               

test_example.py F  # 테스트 통과에 실패했습니다.                                                                                                                                              [100%]

========================== FAILURES ==========================
========================== test_add ==========================

    def test_add():
>       assert add(1, 1) == 3
E       assert 2 == 3
E        +  where 2 = add(1, 1)

test_example.py:6: AssertionError
========================== short test summary info ==========================
FAILED test_example.py::test_add - assert 2 == 3
========================== 1 failed in 0.03s ==========================

이번에는 F (Fail) 값을 내며 테스트가 실패했다고 나옵니다. 동시에 코드의 어느 부분에서 테스트가 실패했는지에 대한 정보가 나옵니다.

이렇게 테스트를 구현하는 코드를 테스트 코드라고 흔히 부릅니다.

# 테스트 코드가 필요한 이유

테스트 코드는 프로젝트의 코드를 테스트하기 위해 필요합니다. 하지만 테스트 코드는 단순히 테스트 실행 말고도 다음처럼 더 큰 의미들이 있습니다.

  • 테스트 코드는 코드가 동작하기 위해 필요한 것들과 입/출력을 드러냅니다.
    • 테스트는 테스트하고자 하는 코드의 클라이언트 중심으로 작성합니다. 즉 테스트하고자 하는 코드를 사용하려면 어떤 의존성이 필요한지, 어떤 입력을 주면 어떤 출력을 뱉는지 테스트 코드를 보면 알 수 있습니다.
    • 따라서 테스트 코드는 프로젝트 코드에 대한 가장 정확한 문서가 됩니다. 테스트 코드만 보면, 코드를 돌리는데 필요한 것들을 알 수 있기 때문입니다.
    • 이런 맥락에서, 누군가 개발한 코드를 볼 때 테스트 코드를 먼저 보면 로직을 파악하는 데 도움이 많이 됩니다. 테스트 코드는 이렇게 다른 개발자들을 위한 일종의 배려이기도 합니다.
  • 테스트 코드는 리팩토링과 지속적인 개발을 위해 필수적입니다.
    • 테스트 코드 없이 개발을 계속해서 해나가면, 추가로 개발한 코드가 기존 코드의 어떤 사이드 이펙트를 불러일으키는지 확인하기 어렵습니다.
    • 테스트 코드를 만들어두면, 추가로 코드를 개발할 때마다, 기존 테스트 코드를 모두 실행함으로써 기존 코드의 작동 여부에 사이드 이펙트가 있는 지 빠르게 확인할 수 있습니다.
    • 이런 맥락에서, 테스트 코드는 일종의 안전망입니다. 테스트 코드 없이 개발을 계속해나가면 매번 리팩토링과 기능 개발을 할 때마다 마음을 졸이게 됩니다.
Last Updated: 2/20/2022, 1:51:31 PM

CC-BY-NC-ND-4.0 Licensed | Copyright © 2021-present Grab